package nl.virtualbookshelf.dao

import scala.io.Source
import collection.mutable.ArrayBuffer
import java.text.SimpleDateFormat
import java.util.Date
import org.springframework.stereotype.Repository
import nl.virtualbookshelf.domain.{Book, Author}

@Repository
class CsvBookDao {
  private val LINE_FEED = "&cr;"
  private val CSV_DELIMITER = "',"
  private val AUTHOR_DELIMITER = ", "
  private val AMPERSANT = "'"

  private val formatter = new SimpleDateFormat("dd-MM-yyyy")

  /**
   * Loads textfile from classpath, and splits per line. Then,
   * each line is splitted once more into sanitized strings. Maps
   * the resulting strings to books.
   */
  def parse(filename: String): Array[Book] = {
    val books = ArrayBuffer[Book]()
    val stream = this.getClass.getClassLoader.getResource(filename)
    Source.fromURL(stream).getLines().map(_.split(CSV_DELIMITER)).foreach(parts => {
      books += mapBook(parts.map(sanitize(_)))
    })
    books.toArray
  }

  /**
   * Maps array of strings to array of books.
   * Assumes the following order:
   *
   * 0  titles         string  mapped (split into multiple titles at linefeed-character)
   * 1  author         string  mapped (split into multiple authors at linefeed-character)
   * 2  genre          string
   * 3  paperback      integer
   * 4  volgnummer     integer
   * 5  name-of-series string
   * 6  publishDate    date
   * 7  purchaseDate   date    mapped
   * 8  isbn           string  mapped
   * 9  comment        string  mapped (split into multiple comments at linefeed-character)
   */
  protected def mapBook(incoming: Array[String]): Book = {
    val titels = mapSimpleLine(incoming(0))
    val authors = mapAuthors(incoming(1))
    val genre = incoming(2)
    val purchaseDate = mapDate(incoming(7))
    val isbn = incoming(8)
    val comments = mapSimpleLine(incoming(9))
    new Book(titels, authors, genre, isbn, purchaseDate, comments)
  }

  /**
   * Splits a line at linefeed-character and trims the results.
   */
  protected def mapSimpleLine(incoming: String): Array[String] = {
    if (incoming != null)
      incoming.split(LINE_FEED).map({ _.trim})
    else
      null
  }

  /**
   * Converts a string to list of authors. Will split authors
   * at linefeed-character, and will split name/surname at comma
   * (if possible).
   */
  protected def mapAuthors(incoming: String): Array[Author] = {
    val authorNames = mapSimpleLine(incoming)
    var authors = ArrayBuffer[Author]()
    if (authorNames != null){
      authorNames.foreach(authorName => {
        val compositeName = authorName.split(AUTHOR_DELIMITER)
        if (compositeName.size > 1)
          authors += new Author(compositeName.head, compositeName.last)
        else
          authors += new Author(compositeName.head)
      })
    }
    authors.toArray
  }

  /**
   * Converts string in 'dd-MM-yyyy'-format to java.util.Date.
   */
  protected def mapDate(incoming: String): Date = {
    var result: Date = null
    if (incoming != null && !incoming.isEmpty)
      result = formatter.parse(incoming)
    else
      result = null
    result
  }

  /**
   * Removes leading and trailing ampersants/whitespace from input.
   */
  protected def sanitize(incoming: String): String = {
    var subject = incoming.replaceAllLiterally("&amp;","'")
                          .replaceAllLiterally("\"","'")
                          .replaceAllLiterally("''","'")

    if (subject.startsWith(AMPERSANT))
      subject = subject.substring(1, subject.length)
    if (subject.endsWith(AMPERSANT))
      subject = subject.substring(0, subject.length - 1)

    if (subject.length > 0)
      subject.trim()
    else
      null
  }
}
